package com.fyakm.javaprojectanalysis.common.util;


import com.fyakm.javaprojectanalysis.common.base.F_Class;
import com.fyakm.javaprojectanalysis.common.base.F_Package;
import com.github.javaparser.JavaParser;
import com.github.javaparser.ParseResult;
import com.github.javaparser.Range;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.ImportDeclaration;
import com.github.javaparser.ast.Node;
import com.github.javaparser.ast.PackageDeclaration;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.expr.SimpleName;

import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;


/**
 * 用于解析java文件的工具类
 */
public class JavaFileParserUtil {

    private final static List<String> importAllList = new ArrayList<>();

    /**
     * 根据文件解析出编译单元
     *
     * @param file 文件
     * @return 编译单元
     */
    private static CompilationUnit getCompilationUnit(File file) {
        CompilationUnit compilationUnit = null;
        JavaParser javaParser = new JavaParser();
        try {
            ParseResult<CompilationUnit> parse = javaParser.parse(file);
            Optional<CompilationUnit> result = parse.getResult();
            compilationUnit = result.get();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        return compilationUnit;
    }

    /**
     * 递归获取目录下的所有文件
     *
     * @param file
     * @return
     */
    public static List<File> getAllFile(File file) {
        List<File> fileList = new ArrayList<>();
        if (file.isFile()) {
            fileList.add(file);
        } else if (file.isDirectory()) {
            File[] files = file.listFiles();
            File[] childrenList = files;
            for (File children : childrenList) {
                List<File> allFile = getAllFile(children);
                fileList.addAll(allFile);
            }
        }
        return fileList;
    }

    public static F_Package getPackage(File file) {
        return getPackage(file, null);
    }

    /**
     * 获取目录下的包，此方法会获取该包下的所有子包和包下的类
     *
     * @param file 文件、目录
     * @return 目录包
     * @Param f_package 上一级的包
     */
    private static F_Package getPackage(File file, F_Package f_package) {
        F_Package result = new F_Package();
        if (file.isFile() && file.getName().endsWith(".java")) {
            CompilationUnit compilationUnit = getCompilationUnit(file);
            if (f_package == null) {
                Optional<PackageDeclaration> packageDeclaration = compilationUnit.getPackageDeclaration();
                String packageName = packageDeclaration.toString();
                result.setName(packageName);
                result.setPackageName(packageName);
                result.setParentPackageName(null);
            }

            String packageName = null;
            List<String> importList = new ArrayList<>();
            F_Class f_class = new F_Class();
            List<Node> childNodeList = compilationUnit.getChildNodes();
            List<String> methodList = new ArrayList<>();
            for (Node node : childNodeList) {
                if (node.getClass().equals(ClassOrInterfaceDeclaration.class)) {
                    ClassOrInterfaceDeclaration declaration = (ClassOrInterfaceDeclaration) node;
                    String classN = declaration.getName().toString();
                    f_class.setClassName(declaration.getFullyQualifiedName().get());
                    f_class.setName(classN);
                    f_class.setImportList(importList);
                    f_class.setPackageName(packageName);
                    Range range = node.getRange().get();
                    f_class.setLine(range.getLineCount());
                    List<MethodDeclaration> methods = declaration.getMethods();
                    List<SimpleName> collect = methods.stream().map(MethodDeclaration::getName).collect(Collectors.toList());
                    List<String> mList = collect.stream().map(SimpleName::toString).collect(Collectors.toList());
                    methodList.addAll(mList);
                    f_class.setMethodList(methodList);
                    f_package.addClassToClassList(f_class);
                    result = f_package;
                } else if (node.getClass().equals(PackageDeclaration.class)) {
                    PackageDeclaration packageDeclaration = (PackageDeclaration) node;
                    packageName = packageDeclaration.getName().toString();
                    f_package.setPackageName(packageName);
                } else if (node.getClass().equals(ImportDeclaration.class)) {
                    // 导包声明语句
                    ImportDeclaration importDeclaration = (ImportDeclaration) node;
                    List<Node> childNodes = importDeclaration.getChildNodes();
                    for (Node childNode : childNodes) {
                        String importPackage = childNode.toString();
                        importList.add(importPackage);
                        if (!importAllList.contains(importPackage)) {
                            importAllList.add(importPackage);
                        }
                    }
                }
            }
        } else if (file.isDirectory()) {

            File[] files = file.listFiles();
            File[] fileList = files;
            String packageName = file.getName();
            result.setName(packageName);
            result.setParentPackageName(f_package);
            for (File children : fileList) {
                F_Package aPackage = getPackage(children, result);
                if (children.isFile()) {
                    if (children.getName().endsWith(".java")) {
                        result.setClassList(aPackage.getClassList());
                    }
                } else if (children.isDirectory()) {
                    result.addPackageToPackageList(aPackage);
                }
            }
        }
        return result;
    }

    /**
     * 导出导出文件到txt文件中，需要此前进行Java文件解析
     *
     * @param director       需要解析的路径
     * @param exportFilePath 导出文件目录
     */
    public static void exportImportPackageToTxt(File director, String exportFilePath) {
        importAllList.clear();
        getPackage(director);
        importAllList.sort(null);
        try (BufferedWriter writer = new BufferedWriter(new FileWriter(exportFilePath))) {
            for (String importPackage : JavaFileParserUtil.importAllList) {
                System.out.println("加入包：" + importPackage);
                writer.write(importPackage + "\n");
            }
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("写入文件时发生错误！");
        }
    }


    // TODO: 2024/5/12 写一个工具方法，可以解析java方法的调用堆栈，可以很好的反应方法中调用的所有方法

}
